package com.zbest.component.dal.sequence;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 默认的流水号生成实现，单数据源模式
 *
 * @author czhou3306
 * @version $Id: DefaultSequenceFactory.java, v 0.1 2014-11-30 下午3:49:26 czhou3306 Exp $
 */
public class DefaultSequenceFactory {

    private static final Logger logger = LoggerFactory
            .getLogger(DefaultSequenceFactory.class);
    private final Lock lock = new ReentrantLock();

    /**
     * 存放sequence，key为sequence名称，value为对应的DefaultSequence对象
     */
    private Map<String, DefaultSequence> sequenceMap = new ConcurrentHashMap<String, DefaultSequence>();

    /**
     * 获取sequence的DAO对象，在所有的factory里共用同一个DAO
     */
    private DefaultSequenceDao defaultSequenceDao;
    private boolean useThreadLocalSeq = false;

    public void setUseThreadLocalSeq(boolean useThreadLocalSeq) {
        this.useThreadLocalSeq = useThreadLocalSeq;
    }

    /**
     * 初始化multipleSequence的工厂
     * 从数据源里获取sequence的记录，对每一条记录进行处理，生成对应的multipleSequence对象，加载到内存中
     *
     * @throws Exception
     */
    public void init() {
        if (defaultSequenceDao == null) {
            throw new IllegalArgumentException("The sequenceDao is null!");
        }
        defaultSequenceDao.validateTableColumn();
        initSequenceMap();
    }

    /**
     * 初始化 sequenceMap,将db里的记录初始化到sequenceMap里
     */
    private void initSequenceMap() {
        //获取全部的sequence记录
        try {
            Map<String, Map<String, Object>> sequenceRecords = defaultSequenceDao
                    .getAllSequenceRecordName();
            if (sequenceRecords == null) {
                throw new IllegalArgumentException("The sequenceRecord is null!");
            }
            for (Map.Entry<String, Map<String, Object>> sequenceRecord : sequenceRecords.entrySet()) {
                String seqName = sequenceRecord.getKey().trim();
                DefaultSequence defaultSequence;
                if (useThreadLocalSeq) {
                    defaultSequence = new ThreadLocalSequence(defaultSequenceDao, seqName);
                } else {
                    defaultSequence = new DefaultSequence(defaultSequenceDao, seqName);
                }
                try {
                    sequenceMap.put(seqName, defaultSequence);
                } catch (Exception e) {
                    logger.error("init the sequenceName = " + seqName + " exception", e);
                }
            }
        } catch (Exception e) {
            logger.error("init the multiple-Sequence-Map failed!", e);
        }
    }

    /**
     * 外部调用接口，根据sequence name 获取sequence value
     * 如果该sequence在multipleSequenceMap里不存在，则去db里查一下是否存在，
     * 如果存在就生成对应的multipleSequence对象并加载到内存，否则报错；
     *
     * @param sequenceName
     * @return
     * @throws Exception
     */
    public long getNextValue(String sequenceName) throws Exception {

        if (sequenceName == null) {
            throw new IllegalArgumentException("The sequence name is null!");
        }
        DefaultSequence defaultSequence = sequenceMap.get(sequenceName);
        if (defaultSequence != null) {
            return defaultSequence.nextValue();
        } else {
            try {
                lock.lock();
                if (sequenceMap.get(sequenceName) == null) {
                    initOneSequenceRecord(sequenceName);
                }
                return sequenceMap.get(sequenceName).nextValue();
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * 根据sequence name初始化单条记录到multipleSequenceMap
     *
     * @param sequenceName sequence name
     * @throws Exception
     */
    private void initOneSequenceRecord(String sequenceName) throws Exception {
        try {
            //获取一条sequence记录
            Map<String, Map<String, Object>> sequenceRecords = defaultSequenceDao
                    .getSequenceRecordByName(sequenceName);
            if (sequenceRecords == null) {
                throw new IllegalArgumentException("The sequenceRecord is null,sequenceName="
                        + sequenceName);
            }
            for (Map.Entry<String, Map<String, Object>> sequenceRecord : sequenceRecords.entrySet()) {
                String seqName = sequenceRecord.getKey().trim();
                DefaultSequence defaultSequence = new DefaultSequence(defaultSequenceDao, seqName);
                sequenceMap.put(seqName, defaultSequence);
            }
        } catch (Exception e) {
            logger.error("init the multipleSequenceMap failed!", e);
            throw e;
        }
    }

    /**
     * Setter method for property <tt>defaultSequenceDao</tt>.
     *
     * @param defaultSequenceDao value to be assigned to property defaultSequenceDao
     */
    public void setDefaultSequenceDao(DefaultSequenceDao defaultSequenceDao) {
        this.defaultSequenceDao = defaultSequenceDao;
    }

}
